<template>
    <n-form
        :model="formState"
        ref="formRef"
        label-placement="left"
        label-align="right"
        label-width="5rem"
    >
        <template v-for="(item, index) of formLayout" :key="item.key">
            <div class="layout" :class="{ design }">
                <n-space class="design-btn">
                    <n-button
                        type="info"
                        size="tiny"
                        @click="onLayoutEdit(index)"
                        >修改</n-button
                    >
                    <n-button
                        type="error"
                        size="tiny"
                        @click="onLayoutDelete(index)"
                        >删除</n-button
                    >
                </n-space>

                <n-form-item
                    :path="item.key"
                    :label="item.label"
                    :rule="item.rule ? JSON.parse(item.rule) : []"
                >
                    <template v-if="edit">
                        <!-- 单行文本、多行文本、密码 -->
                        <n-input
                            v-if="item.type === 'Input'"
                            clearable
                            :disabled="design"
                            v-model:value="formState[item.key]"
                            v-bind="item.props ? JSON.parse(item.props) : {}"
                        />

                        <!-- 数字 -->
                        <n-input-number
                            v-if="item.type === 'InputNumber'"
                            clearable
                            :disabled="design"
                            v-model:value="formState[item.key]"
                            v-bind="item.props ? JSON.parse(item.props) : {}"
                        />

                        <!-- 图片 -->
                        <UploadImage
                            v-if="item.type === 'UploadImage'"
                            :disabled="design"
                            v-model:value="formState[item.key]"
                            v-bind="item.props ? JSON.parse(item.props) : {}"
                        />

                        <!-- 富文本 -->
                        <TinymceEditor
                            v-if="item.type === 'TinymceEditor'"
                            :disabled="design"
                            v-model:value="formState[item.key]"
                            v-bind="item.props ? JSON.parse(item.props) : {}"
                        />

                        <!-- [{"label":"正常","value":1},{"label":"冻结","value":0}] -->
                        <!-- 下拉 -->
                        <n-select
                            v-if="item.type === 'Select'"
                            clearable
                            filterable
                            :disabled="design"
                            v-model:value="formState[item.key]"
                            v-bind="item.props ? JSON.parse(item.props) : {}"
                        />

                        <!-- 单选组 -->
                        <RadioGroup
                            v-if="item.type === 'RadioGroup'"
                            v-model:value="formState[item.key]"
                            v-bind="item.props ? JSON.parse(item.props) : {}"
                        />

                        <!-- 复选 -->
                        <CheckboxGroup
                            v-if="item.type === 'CheckboxGroup'"
                            v-model:value="formState[item.key]"
                            v-bind="item.props ? JSON.parse(item.props) : {}"
                        />
                    </template>

                    <template v-else>
                        <!-- 输入框 -->
                        <n-p
                            v-if="['Input', 'InputNumber'].includes(item.type)"
                            >{{ formState[item.key] }}</n-p
                        >

                        <!-- 图片 -->
                        <n-image
                            v-if="item.type === 'UploadImage'"
                            width="100"
                            :src="formState[item.key]"
                        />

                        <!-- 多选 -->
                        <template v-if="['CheckboxGroup'].includes(item.type)">
                            {{
                                item.props
                                    ? JSON.parse(item.props)
                                          .options.filter((i: any) =>
                                              formState[item.key].includes(
                                                  i.value
                                              )
                                          )
                                          .map((i: any) => i.label)
                                          .join(' , ')
                                    : '-'
                            }}
                        </template>

                        <!-- 单选 -->
                        <template
                            v-if="['RadioGroup', 'Select'].includes(item.type)"
                        >
                            {{
                                item.props
                                    ? JSON.parse(item.props).options.find(
                                          (i: any) =>
                                              i.value == formState[item.key]
                                      ).label
                                    : '-'
                            }}
                        </template>

                        <!-- 编辑器 -->
                        <template v-if="item.type === 'TinymceEditor'">
                            <div v-html="formState[item.key]"></div>
                        </template>
                    </template>
                </n-form-item>
            </div>
        </template>
    </n-form>

    <ModalForm
        v-model:value="layoutFormState"
        :items="layoutFormItems"
        v-model:show="layoutFormShow"
        @submit="onLayoutSubmit"
    />
</template>

<script setup lang="ts">
import { computed, onMounted, PropType, ref } from 'vue'
import { FormDesignItem, FormItemsConfig, FormStateType } from '../types'
import { UploadImage } from '@/components/upload'
import { TinymceEditor } from '@/components/editor'
import ModalForm from './ModalForm.vue'
import { FormInst } from 'naive-ui'
import Sortable from 'sortablejs'

const emits = defineEmits<{
    'update:value': [newValue: FormStateType]
    'update:layout': [newValue: FormDesignItem[]]
    'update:design': [newValue: boolean]
}>()

const props = defineProps({
    value: {
        type: Object as PropType<FormStateType>,
        default: () => ({})
    },
    layout: {
        type: Array as PropType<FormDesignItem[]>,
        default: () => []
    },
    design: {
        type: Boolean,
        default: () => false
    },
    loading: {
        type: Boolean,
        default: () => false
    },
    edit: {
        type: Boolean,
        default: () => false
    }
})

const setSortable = () => {
    const el = document.getElementsByClassName('n-form')[0] as HTMLElement
    new Sortable(el, {
        draggable: '.design',
        ghostClass: 'blue-background-class',
        animation: 150,
        onEnd: ({ oldIndex, newIndex }) => {
            const item = formLayout.value.splice(oldIndex!, 1)[0]
            formLayout.value.splice(newIndex!, 0, item)
        }
    })
}

onMounted(() => {
    setSortable()
})

const formRef = ref<FormInst | null>(null)
const formState = computed<FormStateType>({
    get: () => props.value,
    set: (val) => emits('update:value', val)
})

const formLayout = computed<FormDesignItem[]>({
    get: () => props.layout,
    set: (val) => emits('update:layout', val)
})

function onLayoutDelete(index: number) {
    const newLayout = [...formLayout.value]
    newLayout.splice(index, 1)
    formLayout.value = newLayout
}

const typeOptions: {
    label: string
    value: FormDesignItem['type']
}[] = [
    {
        label: '文本',
        value: 'Input'
    },
    {
        label: '数字',
        value: 'InputNumber'
    },
    {
        label: '下拉',
        value: 'Select'
    },
    {
        label: '单选组',
        value: 'RadioGroup'
    },
    {
        label: '复选框组',
        value: 'CheckboxGroup'
    },
    {
        label: '图片上传',
        value: 'UploadImage'
    },
    {
        label: '富文本',
        value: 'TinymceEditor'
    }
]

const layoutFormItems = ref<FormItemsConfig<FormDesignItem>>([
    {
        field: 'type',
        label: '类型',
        component: 'Select',
        props: {
            placeholder: '请选择类型',
            options: typeOptions
        },
        rule: { required: true, trigger: ['input', 'blur'] }
    },
    {
        field: 'label',
        label: '名称',
        component: 'Input',
        props: {
            placeholder: '请输入显示名称'
        },
        rule: { required: true, trigger: ['input', 'blur'] }
    },
    {
        field: 'key',
        label: '字段',
        component: 'Input',
        props: {
            placeholder: '请输入字段'
        },
        rule: { required: true, trigger: ['input', 'blur'] }
    },
    {
        field: 'rule',
        label: '验证规则',
        component: 'Input',
        props: {
            placeholder: 'json string 写法'
        },
        rule: {
            validator: (rule, value: string) => {
                if (!value) return true
                try {
                    JSON.parse(value)
                    return true // 如果没有抛出异常，则是有效的 JSON
                } catch {
                    return Promise.reject(
                        new Error(`${(<any>rule).field} must be json string`)
                    )
                }
            },
            trigger: ['input', 'blur']
        }
    },
    {
        field: 'props',
        label: 'Props',
        component: 'Input',
        props: {
            placeholder: 'json string 写法',
            type: 'textarea'
        },
        rule: {
            validator: (rule, value: string) => {
                if (!value) return true
                try {
                    JSON.parse(value)
                    return true // 如果没有抛出异常，则是有效的 JSON
                } catch {
                    return Promise.reject(
                        new Error(`${(<any>rule).field} must be json string`)
                    )
                }
            },
            trigger: ['input', 'blur']
        }
    }
])
const layoutFormShow = ref(false)
const layoutFormDefault: FormDesignItem = {
    type: null as any,
    label: '',
    key: '',
    rule: '',
    props: ''
}
const layoutFormState = ref<FormDesignItem>({ ...layoutFormDefault })
const layoutFormIndex = ref<number>()
function onLayoutEdit(index: number) {
    layoutFormIndex.value = index
    layoutFormState.value = { ...formLayout.value[index] }
    layoutFormShow.value = true
}
function onLayoutSubmit() {
    const index = layoutFormIndex.value

    // 处理默认值
    const props = layoutFormState.value.props
    if (props) {
        const propsObj = JSON.parse(props)
        if (propsObj['default-value']) {
            const key = layoutFormState.value.key
            const value = Number(propsObj['default-value'])
            formState.value[key] = isNaN(value)
                ? propsObj['default-value']
                : value
        }
    }

    if (typeof index === 'undefined') {
        formLayout.value.push(layoutFormState.value)
    } else {
        formLayout.value[index] = layoutFormState.value
    }

    layoutFormShow.value = false
}

// 新增
function add() {
    layoutFormIndex.value = undefined
    layoutFormState.value = { ...layoutFormDefault }
    layoutFormShow.value = true
}

defineExpose({
    add,
    validate: () => formRef.value?.validate()
})
</script>

<style lang="less" scoped>
.layout {
    position: relative;
    box-sizing: border-box;
    &.design {
        padding: 1rem;
        border-radius: var(--n-border-radius);
        transition: background-color 0.3s var(--n-bezier);

        &:hover {
            background-color: rgb(237, 245, 254);
            border: 1px dashed rgba(199, 223, 251, 1);
            .design-btn {
                z-index: 9;
                position: absolute;
                top: 0;
                right: 0;
                display: flex !important;
            }
        }
    }
    .design-btn {
        display: none !important;
    }
}
</style>
